昨日我們講解當Transaction出現Concurrent時會造成什麼樣的問題,透過Spring提供的Isolation屬性可以解決對應的問題。
今日我們將介紹@Transactional當中另一個重要的屬性Propagation
當事務中還有事務時,子事務是否要跟大事務共用一個事務。例如transferMoney是一個大事務,子事務addMoney、minusMoney是否要與transferMoney共用事務。
@Transactional()
public void transferMoney(String from, String to , Integer amount) throws FileNotFoundException {
accountDao.addMoney(to,amount);
accountDao.minusMoney(from,amount);
System.out.println("轉帳完成:"+from+" to "+to+" 金額:"+amount);
}
Types | Description |
---|---|
REQUIRED | Support a current transaction, create a new one if none exists. |
REQUIRED_NEW | Create a new transaction, and suspend the current transaction if one exists. |
SUPPORTS | Support a current transaction, execute non-transactionally if none exists. |
NOT_SUPPORTED | Execute non-transactionally, suspend the current transaction if one exists. |
MANATORY | Support a current transaction, throw an exception if none exists. |
NEVER | Execute non-transactionally, throw an exception if a transaction exists. |
NESTED | Execute within a nested transaction if a current transaction exists, behave like REQUIRED otherwise. |
@Service
public class AccountService {
@Autowired
private AccountDao accountDao;
@Transactional(propagation = Propagation.REQUIRED)
public void transferMoney(String from, String to , Integer amount) {
//Callee(1)
accountDao.addMoney(to,amount);
//Callee(2)
accountDao.minusMoney(from,amount);
System.out.println("轉帳完成:"+from+" to "+to+" 金額:"+amount);
}
}
@Repository
public class AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Transactional(propagation = Propagation.REQUIRED)
public void addMoney(String accountId, Integer amount) {
String sql = "UPDATE ACCOUNT SET BALANCE = BALANCE +? WHERE ACCOUNT_ID = ?";
jdbcTemplate.update(sql, amount, accountId);
}
@Transactional(propagation = Propagation.REQUIRED)
public void minusMoney(String accountId, Integer amount) {
String sql = "UPDATE ACCOUNT SET BALANCE = BALANCE -? WHERE ACCOUNT_ID = ?";
jdbcTemplate.update(sql, amount, accountId);
int i = 10/0;
}
}
@Test
public void testDay29() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("bean29.xml");
AccountService service = ioc.getBean(AccountService.class);
service.transferMoney("1","2",100);
}
Result
Transaction rollback帳戶皆不變
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addMoney(String accountId, Integer amount) {
String sql = "UPDATE ACCOUNT SET BALANCE = BALANCE +? WHERE ACCOUNT_ID = ?";
jdbcTemplate.update(sql, amount, accountId);
}
Result
由於addMoney是開新的Transaction所以完成就代表該Transaction commit,所以callee(2)的exception影響不到callee(1)
@Transactional(propagation = Propagation.REQUIRED)
public void transferMoney(String from, String to , Integer amount) {
accountDao.addMoney(to,amount);
accountDao.minusMoney(from,amount);
int i = 10/0;
System.out.println("轉帳完成:"+from+" to "+to+" 金額:"+amount);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addMoney(String accountId, Integer amount) {
String sql = "UPDATE ACCOUNT SET BALANCE = BALANCE +? WHERE ACCOUNT_ID = ?";
jdbcTemplate.update(sql, amount, accountId);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void minusMoney(String accountId, Integer amount) {
String sql = "UPDATE ACCOUNT SET BALANCE = BALANCE -? WHERE ACCOUNT_ID = ?";
jdbcTemplate.update(sql, amount, accountId);
}
Result
由於子事務皆是REQUIRED_NEW所以結束即代表Transaction commit 所以caller最後拋的異常並不影響子事務。
@Transactional(propagation = Propagation.REQUIRED)
public void transferMoney2(String from, String to , Integer amount) {
addMoney(to,amount);
minusMoney(from,amount);
int i = 10/0;
System.out.println("轉帳完成:"+from+" to "+to+" 金額:"+amount);
}
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void addMoney(String to,Integer amount){
accountDao.addMoney(to,amount);
}
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void minusMoney(String from,Integer amount){
accountDao.minusMoney(from,amount);
}
@Repository
public class AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void addMoney(String accountId, Integer amount) {
String sql = "UPDATE ACCOUNT SET BALANCE = BALANCE +? WHERE ACCOUNT_ID = ?";
jdbcTemplate.update(sql, amount, accountId);
}
public void minusMoney(String accountId, Integer amount) {
String sql = "UPDATE ACCOUNT SET BALANCE = BALANCE -? WHERE ACCOUNT_ID = ?";
jdbcTemplate.update(sql, amount, accountId);
}
@Test
public void testDay29() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("bean29.xml");
AccountService service = ioc.getBean(AccountService.class);
service.transferMoney2("1","2",100);
}
Result
AccountService的transferMoney2 method調用本地方法非用代理對象,故只會有一個Transaction